#include "utils.hpp"
#include "literal.hpp"
#include "entity.hpp"

#include <algorithm>

namespace mashine { namespace kb {

using namespace std;

Literal::Literal() : Entity<string>("", vector<string>())
{
	// APP_LOGGER(info) << "Literal {" << "empty" << "} instantiated";
}

Literal::Literal(string name, vector<string> params, bool denied)
		: Entity<string>(name, params)
{
	this->denied = denied;
	
	// APP_LOGGER(info) << "Literal {" << toString() << "} instantiated";
}

Literal::Literal(Literal const & literal) : Entity<string>(literal)
{
	this->denied = literal.denied;
	
	// APP_LOGGER(info) << "Literal {" << literal.toString() << "} copied";
}

Literal::~Literal()
{
	// APP_LOGGER(info) << "Literal {" << toString() << "} begins to break down";
}

/**
 * need owerride this method in child class 
 * to correct work Entity constructor
 */
bool Literal::validateName() const
{
	// APP_LOGGER(info) << "Literal {" << toString() << "} name validating begin";
	
	return utils::is_Literal_name(name);
}

/**
 * need owerride this method in child class 
 * to correct work Entity constructor
 */
bool Literal::validateParam() const
{
	// APP_LOGGER(info) << "Literal {" << toString() << "} params validating begin";
	
	for (vector<string>::const_iterator i = params.begin(); i != params.end(); ++i)
    {
        if (utils::is_variable_name(*i) || utils::is_constant_name(*i) ) {} else 
		{
			return false;
		}
    }
	
	return true;
}

bool Literal::isFact() const
{
    for (vector<string>::const_iterator i = params.begin(); i != params.end(); ++i) 
    {
        if (utils::is_variable_name(*i))
        {
            return false;
        }
    }

    return true;
}

bool Literal::isDenied() const
{
	return denied;
}

void Literal::setDenied(bool denied)
{
	this->denied = denied;
}

string Literal::toString() const
{
	if (name.length() == 0) 
		return "empty";
		
    string ret;

	if (denied) ret += "~";
	
    ret += name;
    ret += "(";

    vector<string>::const_iterator i;
    for (i = params.begin(); i != params.end(); i++)
    {
		ret += *i + ", ";
    }
	
	if (params.size() != 0) 
	{
		ret.erase(ret.size() - 2, 2);
    }
	
    return ret + ")";
}

Literal Literal::operator-() const
{
	// this solution maby not work on any compilers
	return Literal(this->name, this->params, !this->denied);
}

// Literal & operator = (const Literal & l)
// {
	// this->name = l.name;
	// this->params = l.params;
	
	// return *this;
// }

bool Literal::operator == ( Literal const & l) const
{
	if (getName().compare(l.getName()))
		return false;

	if (getParams().size() != l.getParams().size())
		return false;
	
	std::map<std::string, std::string> pv;			// maping varibale to possible const
	std::vector<int> undefIndexes;
	for (int i = 0; i < params.size(); ++i)
	{
		// both param is constant
		if (utils::is_constant_name(params[i]) && utils::is_constant_name(l.getParams()[i])
				&& params[i].compare(l.getParams()[i]))
		{
			std::cout << toString() << " <><> " << l.toString();
			return false;
		}
		// both variable
		else if (utils::is_variable_name(params[i]) && utils::is_variable_name(l.getParams()[i]))
		{
			bool isFistContains = pv.find(params[i]) != pv.end();
			bool isSecondContains = pv.find(l.getParams()[i]) != pv.end();
			
			if (isFistContains && isSecondContains)
			{
				if (pv[params[i]].compare(l.getParams()[i]))
				{
					return false;
				}
				else
				{
					undefIndexes.push_back(i);
				}
			} 
			else if (isFistContains)
			{
				pv[l.getParams()[i]] = pv[params[i]];
			}
			else if (isSecondContains)
			{
				pv[params[i]] = pv[l.getParams()[i]];
			}
			else if (!(isSecondContains || isFistContains))
			{
				undefIndexes.push_back(i);
			}
		} 
		// one variable from params
		else if (utils::is_variable_name(params[i]) && pv.find(params[i]) != pv.end())
		{
			pv[params[i]] = l.getParams()[i];
		}
		else if (utils::is_variable_name(l.getParams()[i]) && pv.find(l.getParams()[i]) != pv.end())
		{
			pv[l.getParams()[i]] = params[i];
		}
	}
	while (undefIndexes.size() != 0)
	{
		int i = undefIndexes.back();
		
		bool isFistContains = pv.find(params[i]) != pv.end();
		bool isSecondContains = pv.find(l.getParams()[i]) != pv.end();
		
		if (isFistContains && isSecondContains)
		{
			if (pv[l.getParams()[i]].compare(pv[params[i]])) return false;
		}
		
		// else if (isFistContains)
		// {
			
		// }
		// else if (isSecondContains)
		// {
		// }
		
		undefIndexes.pop_back();
	}
	
	std::cout << toString() << " ==== " << l.toString();
	
	return true;
}

bool Literal::operator != (const Literal & l) const
{
	return *this == l;
}

}}